#include "ogrebindingPCH.h"
#include "ogrebinding.h"
#include "scenebinding.h"
#include "entitybinding.h"
#include "camerabinding.h"
#include "clientbinding.h"
#include "vector3binding.h"
#include "quaternionbinding.h"
#include "lightbinding.h"
#include "renderwindowbinding.h"

namespace lua_ogre {

lua_State *g_lua;

static int RenderOneFrame(lua_State *ls)
{
	Ogre::Root::getSingleton().renderOneFrame();
	return 0;
}

static int CreateRenderWindow(lua_State *ls)
{
	const char *name;
	if (lua_isstring(ls, 1))
	{
		name = lua_tostring(ls, 1);
	}
	else
	{
		LogLuaError << "ov.createrenderwindow() failed! invalid render window name for first parameter";
		return 0;
	}

	int width, height;
	if (lua_isnumber(ls, 2) && lua_isnumber(ls, 3))
	{
		width = lua_tointeger(ls, 2);
		height = lua_tointeger(ls, 3);
	}
	else
	{
		LogLuaError << "ov.createrenderwindow() failed! invalid width / height";
		return 0;
	}

    Ogre::RenderWindow *rw(0);
	unsigned int wnd_handle;
	if (lua_isuserdata(ls, 4))
	{
		wnd_handle = (unsigned int)lua_touserdata(ls, 4);

        Ogre::NameValuePairList params;
        params.insert(std::make_pair("externalWindowHandle", Ogre::StringConverter::toString(wnd_handle)));
        rw = Ogre::Root::getSingleton().createRenderWindow(
            name, width, height, false, &params);
    }
    else
    {
        rw = Ogre::Root::getSingleton().createRenderWindow(name, width, height, false);
        rw->setActive(true);
        rw->setVisible(true);
    }
    
	if (0 == rw)
	{
		LogLuaError << "not enough memory";
		return 0;
	}

	LuaRenderWindow *mem = (LuaRenderWindow*)lua_newuserdata(ls, sizeof(LuaRenderWindow));
	if (NULL == mem)
	{
		LogLuaError << "not enough memory";
		return 0;
	}
    new(mem) LuaRenderWindow(rw);
	luaL_getmetatable(ls, RenderWindowClassName);
	lua_setmetatable(ls, -2);
	return 1;
}

static int DestroyRenderWindow(lua_State *ls)
{
	if (lua_gettop(ls) != 1
		|| !luaL_checkudata(ls, 1, RenderWindowClassName))
	{
		LogLuaError << "ov:DestroyRenderWindow(RenderWindow) failed!";
		return 0;
	}

	Ogre::Root::getSingleton().destroyRenderTarget(
		((LuaRenderWindow*)lua_touserdata(ls, 1))->c_obj);
	return 0;
}

static luaL_Reg g_ogre_methods[] = {
	{"CreateRenderWindow", CreateRenderWindow},
	{"RenderOneFrame", RenderOneFrame},
	{"DestroyRenderWindow", DestroyRenderWindow},
	{NULL, NULL}
};

bool BindOgre(lua_State *ls)
{
	g_lua = ls;
	luaL_register(ls, "ov", g_ogre_methods);
	lua_pop(ls, 1);

	BindScene(ls);
	BindEntity(ls);
	BindCamera(ls);
	BindClient(ls);
	BindVector3(ls);
	BindQuaternion(ls);
	BindLight(ls);
	BindRenderWindow(ls);

	if (lua_gettop(ls) != 0)
	{
		return false;
	}
	return true;
}
    
bool LoadModuleFromFile(lua_State *ls, const char *path, const char *module_name)
{
    if (NULL == path || path[0] == 0)
    {
        LogLuaError << "invalud module path";
		return false;
    }
    
    if (NULL == module_name || module_name[0] == 0)
    {
        LogLuaError << "invalid module name";
		return false;
    }
    
    lua_getfield(ls, LUA_GLOBALSINDEX, module_name);
    if (!lua_isnil(ls, -1))
    {
        LogLuaError << module_name << " module already exist";
		return false;
    }
    else 
	{
        lua_pop(ls, 1);
    }
    
    if (0 != luaL_loadfile(ls, path))
    {
		LogLuaError << "failed to load " << path;
        return false;
    }
    
    lua_pushvalue(ls, -1);
    lua_setfield(ls, LUA_GLOBALSINDEX, module_name);
    
    lua_newtable(ls);
    int chunk_env = lua_gettop(ls);
    
    luaL_newmetatable(ls, module_name);
    lua_pushstring(ls, "__index");
    lua_pushvalue(ls, LUA_GLOBALSINDEX);
    lua_rawset(ls, -3);
    lua_setmetatable(ls, chunk_env);
    
    lua_setfenv(ls, 0);
    
    if (0 != lua_pcall(ls, 0, 0, 0))
    {
        LogLuaError << lua_tostring(ls, -1);
		return false;
    }
    
    return true;
}
    
bool RunVoidFunction(lua_State *ls, const char *module, const char *function)
{
    if (NULL == module || NULL == function)
    {
        return false;
    }
    
    lua_getfield(ls, LUA_GLOBALSINDEX, module);
    if (lua_isnil(ls, -1))
    {
		LogLuaError << "module " << module << " not found!";
        return false;
    }
    lua_getfenv(ls, -1);
    lua_getfield(ls, -1, function);
    bool suc = 0 == lua_pcall(ls, 0, 0, 0);
	if (!suc)
    {
		LogLuaError << "failed to call " << module << "." << function << " : " << lua_tostring(ls, -1);
    }
    lua_pop(ls, 2);
    return suc;
}

}